package org.iabako.client.ui.form.user.credit;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTMLPanel;
import org.iabako.client.config.page.PageConfigEnum;
import org.iabako.client.navigation.NavigationController;
import org.iabako.client.tools.IabakoAsyncCallback;
import org.iabako.client.tools.Messages;
import org.iabako.client.ui.form.AbstractForm;
import org.iabako.client.ui.layout.field.*;
import org.iabako.client.ui.layout.template.Popup;
import org.iabako.client.ui.layout.widget.dialogbox.IabakoDialogBox;
import org.iabako.client.ui.layout.widget.paypal.PayPalForm;
import org.iabako.client.ui.layout.widget.user.balanceaccount.BalanceAccount;
import org.iabako.client.user.AuthenticateUserProvider;
import org.iabako.shared.entity.business.AbstractEntity;
import org.iabako.shared.entity.business.CustomField;
import org.iabako.shared.entity.enumeration.IabakoPackage;
import org.iabako.shared.entity.user.User;
import org.iabako.shared.entity.user.UserCredit;
import org.iabako.shared.entity.user.UserPayPalToken;
import org.iabako.shared.entity.user.UserPayPalTokenUser;
import org.iabako.shared.iservice.business.CustomFieldServiceAsync;
import org.iabako.shared.iservice.user.UserServiceAsync;
import org.iabako.shared.tools.GenericTools;

import java.util.*;

/**
 * Created by jose on 23/07/15.
 */
public abstract class BuyCreditForm extends AbstractForm {

    interface MyUiBinder extends UiBinder<HTMLPanel, BuyCreditForm> {
    }

    protected MyUiBinder binderForm = GWT.create(MyUiBinder.class);
    protected final UserServiceAsync userServiceAsync = UserServiceAsync.Util.getInstance();
    protected final CustomFieldServiceAsync customFieldService = CustomFieldServiceAsync.Util.getInstance();

    @UiField
    SelectList iabakoPackage;
    @UiField
    LabelText changePackageValues;
    @UiField
    HTMLPanel usersPanel;
    @UiField
    InputTextInteger howManyField;
    @UiField
    LabelText total;
    @UiField
    LabelText viewPackageDescription;
    @UiField
    HTMLPanel usersTable;
    @UiField
    Button confirmationButton;
    @UiField
    HTMLPanel buyCreditPanel;
    @UiField
    HTMLPanel payPalPanel;
    @UiField
    PayPalForm payPalForm;
    @UiField
    HTMLPanel optionsPanel;
    @UiField
    LabelText upgradeOption;
    @UiField
    LabelText buyCreditOption;
    @UiField
    HTMLPanel principalPanel;
    @UiField
    HTMLPanel principalUserDatesPanel;
    @UiField
    LabelText packageLabel;
    @UiField
    LabelText availableLabel;
    @UiField
    LabelText principalUserName;
    @UiField
    HTMLPanel suggestionPanel;
    @UiField
    LabelText suggestionBalanceLabel;
    @UiField
    LabelText suggestionCustomFieldsLabel;
    @UiField
    LabelText principalUserDates;
    @UiField
    LabelText suggestionPackageLabel;
    @UiField
    LabelText buyCreditUsersAction;
    @UiField
    LabelText creditGreaterThanEnterpriseCredit;
    @UiField
    HTMLPanel usersTableNote;
    @UiField
    HTMLPanel principalUserDatesNote;
    @UiField
    HTMLPanel totalPanel;

    User principalUser;
    UserCredit principalUserCredit;
    IabakoPackage suggestionPackage;
    Date enterpriseCreditEnds;
    Map<User, SelectList> userListOfMonths = new HashMap<User, SelectList>();
    boolean packageChanged;
    List<CustomField> enterpriseCustomFields;

    public BuyCreditForm() {
        simplePopup = true;
        setForm(binderForm, PageConfigEnum.BuyCredit, null);
        Popup.resizeToMediumPopup();

        setAnnualPackages(false);
        enterpriseCustomFields = AuthenticateUserProvider.getUser().getEnterprise().getCustomFields();
        principalUser = AuthenticateUserProvider.getUser().getEnterprise().getPrincipalUser();
        principalUserCredit = principalUser.getUserCredit();

        packageLabel.setHtmlLabel(Messages.getLabel("buy_credit_package_label", principalUserCredit.getIabakoPackage().getShortLabel()));
        availableLabel.setHtmlLabel(Messages.getLabel("balance_account_available", GenericTools.formatDate(getPrincipalExpirationDate())));
        iabakoPackage.setRawEnumValue(principalUserCredit.getIabakoPackage());
        principalUserName.setHtmlLabel(Messages.getLabel("buy_credit_principal_user", principalUser.getName()));
        enterpriseCreditEnds = getPrincipalExpirationDate();

        calculateView();
        setHandlers();
    }

    private Date getPrincipalExpirationDate() {
        if (packageChanged && howManyField.getIntValue() != null && howManyField.getIntValue() > 0) {
            return new GenericTools().addDaysToDate(GenericTools.getDateDay(), -1);
        } else {
            Date expirationDate = GenericTools.getDateFromString(principalUserCredit.getExpirationDateString());
            return GenericTools.getDateWithoutTime(expirationDate);
        }
    }

    private void calculateView() {
        Date today = GenericTools.getDateDay();
        Date expirationDate = GenericTools.getDateFromString(AuthenticateUserProvider.getUser().getUserCredit().getExpirationDateString());
        if (!principalUserCredit.getIabakoPackage().isFree() && expirationDate.compareTo(today) >= 0) {
            optionsPanel.setVisible(true);
            buyCreditPanel.setVisible(false);
        } else {
            setBuyCreditView();
        }
    }

    private void setHandlers() {
        upgradeOption.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                setBuyUpgradeView();
            }
        });
        buyCreditOption.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                setBuyCreditView();
            }
        });

        setPackageListHandler();

        howManyFieldHandler();

        setConfirmationButtonHandler();

        viewPackageDescription.getLabel().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                Window.open(Window.Location.getHref() + "#PricesTable", "#PricesTable", null);
            }
        });

        buyCreditUsersAction.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {

                if (enterpriseCreditEnds.compareTo(GenericTools.getDateDay()) <= 0) {
                    //Enterprise without credit can not buy credit for users
                    IabakoDialogBox changePackageDialogBox = new IabakoDialogBox("buy_credit_enterprise_without_credit_title", "buy_credit_enterprise_without_credit_message", true, "buy_credit_enterprise_without_credit_ok");
                    changePackageDialogBox.setAvoidClosePopupCall(true);
                    NavigationController.getInstance().show(changePackageDialogBox);
                    howManyField.addErrorMessage(howManyField, true, null);
                    return;
                }
                usersTable.setVisible(true);
                buyCreditUsersAction.setVisible(false);
            }
        });
    }

    public void setBuyUpgradeView() {
        optionsPanel.setVisible(false);
        totalPanel.setVisible(true);
        total.setHtmlLabel(Messages.getLabel("buy_credit_total", GenericTools.formatAmount(9 + "")));
        goToPayPalForm(true);
    }

    private void setPackageListHandler() {
        changePackageValues.getLabel().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                if (changePackageValues.getLabel().getText().equals(Messages.getLabel("buy_credit_change_to_annual_packages"))) {
                    changePackageValues.setLabel("buy_credit_change_to_monthly_packages");
                    setAnnualPackages(true);
                } else {
                    changePackageValues.setLabel("buy_credit_change_to_annual_packages");
                    setAnnualPackages(false);
                }
                calculateTotal();
                resetUserListMonth();
            }
        });

        iabakoPackage.getSelectField().addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent changeEvent) {

                if (!principalUser.getUserCredit().getIabakoPackage().isFree()
                        && principalUser.getUserCredit().getIabakoPackage() != iabakoPackage.getEnumValue()
                        && principalUser.getUserCredit().getIabakoPackage() != IabakoPackage.getPartner((IabakoPackage) iabakoPackage.getEnumValue())) {
                    packageChanged = true;
                } else {
                    packageChanged = false;
                }
                principalUserDatesNote.setVisible(packageChanged);

                calculateTotal();
                resetUserListMonth();
            }
        });
    }

    private void setAnnualPackages(boolean annualPackage) {
        IabakoPackage lastSelectedPackage = (IabakoPackage) iabakoPackage.getEnumValue();

        iabakoPackage.replaceEnumValues("IabakoPackage");

        if (annualPackage) {
            iabakoPackage.removeEnumValue(IabakoPackage.freeOneMonth, IabakoPackage.free, IabakoPackage.starterMonthly, IabakoPackage.proMonthly,
                    IabakoPackage.enterpriseMonthly);
            howManyField.setInputTitle("buy_credit_how_many_year");

        } else {
            iabakoPackage.removeEnumValue(IabakoPackage.freeOneMonth, IabakoPackage.free, IabakoPackage.starterAnnual, IabakoPackage.proAnnual,
                    IabakoPackage.enterpriseAnnual);
            howManyField.setInputTitle("buy_credit_how_many_month");
        }

        if (IabakoPackage.getPartner(lastSelectedPackage) != null) {
            iabakoPackage.setRawEnumValue(IabakoPackage.getPartner(lastSelectedPackage));
        }
    }

    private void howManyFieldHandler() {
        howManyField.getInputField().addKeyUpHandler(new KeyUpHandler() {
            public void onKeyUp(KeyUpEvent keyUpEvent) {
                calculateTotal();
                resetUserListMonth();
            }
        });
    }

    private void setBuyCreditView() {
        principalPanel.add(suggestionPanel);
        principalPanel.add(buyCreditPanel);

        optionsPanel.setVisible(false);
        buyCreditPanel.setVisible(true);
        boolean moreThanOneUser = AuthenticateUserProvider.getUser().getEnterprise().getUsersNumber() > 1;
        if (moreThanOneUser) {
            usersPanel.setVisible(true);
            setUsersList();
        }

        Date expirationMines5 = new GenericTools().addDaysToDate(getPrincipalExpirationDate(), -5);
        if (principalUserCredit.getIabakoPackage().isFree() && GenericTools.getDateWithoutTime().compareTo(expirationMines5) >= 0) {
            activateSuggestionPanel();
        }
    }

    private void setUsersList() {
        userServiceAsync.getUsersBySessionEnterprise(new IabakoAsyncCallback<List<User>>() {
            public void onSuccess(List<User> result) {

                HTMLPanel headerRow = new HTMLPanel("");
                headerRow.setStyleName("userstablerow");

                LabelText header1 = new LabelText("userstablecell userstableheader userstableheadernamecolumn");
                header1.setLabel("buy_credit_users");
                LabelText header2 = new LabelText("userstablecell userstableheader userstableheaderbalancecolumn userstablehowmanyheader");
                header2.setLabel("buy_credit_how_many_month");
                LabelText header3 = new LabelText("userstablecell userstableheader userstableheaderbalancecolumn");
                header3.setLabel("buy_credit_balance_now");
                LabelText header4 = new LabelText("userstablecell userstableheader userstableheaderbalancecolumn");
                header4.setLabel("buy_credit_balance_after");

                headerRow.add(header1);
                headerRow.add(header2);
                headerRow.add(header3);
                headerRow.add(header4);

                usersTable.add(headerRow);

                for (User user : result) {

                    boolean isTheLastOne = result.size() - 1 == result.indexOf(user);

                    if (user.isPrincipal()) {
                        continue;
                    }

                    HTMLPanel row = new HTMLPanel("");
                    row.setStyleName("userstablerow");

                    usersTable.add(row);

                    LabelText userName = new LabelText("userstablecell");
                    String name = user.getName().length() > 25 ? user.getName().substring(0, 25) + "..." : user.getName();

                    Date expirationDate = user.getUserCredit() != null ? GenericTools.getDateFromString(user.getUserCredit().getExpirationDateString()) : null;
                    if (user.getUserCredit() != null && expirationDate.compareTo(GenericTools.getDateWithoutTime()) >= 0) {
                        name += Messages.getLabel("buy_credit_user_available", GenericTools.formatDate(GenericTools.getDateWithoutTime(expirationDate)));
                    } else if (user.getUserCredit() != null) {
                        name += Messages.getLabel("buy_credit_user_expired", GenericTools.formatDate(GenericTools.getDateWithoutTime(expirationDate)));
                    } else {
                        name += Messages.getLabel("buy_credit_user_no_credit");
                    }
                    userName.setHtmlLabel(name);
                    row.add(userName);

                    LabelText available = new LabelText("userstablecell usertablebalancecolumn");
                    available.setLabel("-");
                    LabelText newAvailable = new LabelText("userstablecell usertablebalancecolumn");
                    newAvailable.setLabel("-");

                    String listStyle = "userstablecell usertablebalancecolumn userstablehowmany";
                    listStyle += isTheLastOne ? " userstablehowmanylast" : "";
                    SelectList list = new SelectList(listStyle, "inputsize10", "");
                    userListOfMonths.put(user, list);

                    addValuesToUsersMonthsList(user.getUserCredit(), list);
                    list.getSelectField().addChangeHandler(new CalculateUsersCreditMonths(list, user.getUserCredit(), available, newAvailable));

                    row.add(list);
                    row.add(available);
                    row.add(newAvailable);
                }

                super.onSuccess(result);
            }
        });
    }

    private class CalculateUsersCreditMonths implements ChangeHandler {

        private SelectList list;
        private UserCredit userCredit;
        private LabelText startDateLabel;
        private LabelText endDateLabel;

        private CalculateUsersCreditMonths(SelectList list, UserCredit userCredit, LabelText startDateLabel, LabelText endDate) {
            this.list = list;
            this.userCredit = userCredit;
            this.startDateLabel = startDateLabel;
            this.endDateLabel = endDate;
        }

        public void onChange(ChangeEvent changeEvent) {
            int howManyMonth = Integer.parseInt(list.getValue() + "");
            if (howManyMonth == 0) {
                startDateLabel.setLabel("-");
                endDateLabel.setLabel("-");
                calculateTotal();
                return;
            }

            Date startDate = GenericTools.getDateDay();
            Date expirationDate = userCredit != null ? GenericTools.getDateFromString(userCredit.getExpirationDateString()) : null;
            if (userCredit != null && expirationDate.compareTo(GenericTools.getDateDay()) >= 0) {
                startDate = new GenericTools().addDaysToDate(expirationDate, 1);
            }
            startDateLabel.setLabel(GenericTools.formatDate(startDate));

            Date endDate = new GenericTools().addMonthsToDate((Date) startDate.clone(), howManyMonth);
            endDate = new GenericTools().addDaysToDate(endDate, -1);
            if (endDate.compareTo(enterpriseCreditEnds) > 0) {
                endDate = (Date) enterpriseCreditEnds.clone();
                usersTableNote.setVisible(true);
                creditGreaterThanEnterpriseCredit.setLabel(Messages.getLabel("buy_credit_greater_than_enterprise_credit",
                        GenericTools.formatDate(enterpriseCreditEnds)));
            }
            endDateLabel.setLabel(GenericTools.formatDate(endDate));

            calculateTotal();
        }
    }

    private void addValuesToUsersMonthsList(UserCredit userCredit, SelectList list) {

        Date today = GenericTools.getDateDay();
        Date userCreditFromDB = userCredit != null ? GenericTools.getDateFromString(userCredit.getExpirationDateString()) : null;

        Date newUserCredit = userCreditFromDB != null
                && userCreditFromDB.compareTo(today) >= 0 ? new GenericTools().addDaysToDate(userCreditFromDB, 1) : today;
        newUserCredit = GenericTools.getDateWithoutTime(newUserCredit);

        long index = 0;
        list.addValue(index++, "-");
        while (newUserCredit.compareTo(enterpriseCreditEnds) <= 0) {
            list.getValues().put(index, index + "");
            list.getSelectField().addItem(index + "", index + "");
            newUserCredit = new GenericTools().addMonthsToDate(newUserCredit, 1);
            index++;
        }
    }

    private void resetUserListMonth() {
        usersTableNote.setVisible(false);

        for (User user : userListOfMonths.keySet()) {
            SelectList list = userListOfMonths.get(user);

            //To reset dates labels (by onchange event)
            list.setValue(0L);

            list.clearValues();
            addValuesToUsersMonthsList(user.getUserCredit(), list);
        }
    }

    private void activateSuggestionPanel() {
        suggestionPanel.setVisible(true);

        if (AuthenticateUserProvider.getBalanceAccount() == null) {
            //This will set balance account in static object AuthenticateUserProvider
            new BalanceAccount();
        }

        final int invoiceNumber = AuthenticateUserProvider.getBalanceAccount().enterpriseBalance.getInvoiceTracks().size();
        final int receiptNumber = AuthenticateUserProvider.getBalanceAccount().enterpriseBalance.getReceiptTracks().size();
        final int communicationNumber = AuthenticateUserProvider.getBalanceAccount().enterpriseBalance.getCommunicationTracks().size();

        suggestionBalanceLabel.setHtmlLabel(Messages.getLabel("buy_credit_suggestion_balance",
                invoiceNumber + "", receiptNumber + "", communicationNumber + ""));

        suggestionCustomFieldsLabel.setHtmlLabel(Messages.getLabel("buy_credit_suggestion_custom_fields", enterpriseCustomFields.size() + ""));

        if (invoiceNumber <= IabakoPackage.starterMonthly.getInvoiceLimit()
                && receiptNumber <= IabakoPackage.starterMonthly.getInvoiceLimit()
                && communicationNumber <= IabakoPackage.starterMonthly.getCommunicationLimit()
                && enterpriseCustomFields.size() <= IabakoPackage.starterMonthly.getCustomFieldLimit()) {
            suggestionPackage = IabakoPackage.starterMonthly;

        } else if (invoiceNumber <= IabakoPackage.proMonthly.getInvoiceLimit()
                && receiptNumber <= IabakoPackage.proMonthly.getInvoiceLimit()
                && communicationNumber <= IabakoPackage.proMonthly.getCommunicationLimit()
                && enterpriseCustomFields.size() <= IabakoPackage.proMonthly.getCustomFieldLimit()) {
            suggestionPackage = IabakoPackage.proMonthly;
        } else {
            suggestionPackage = IabakoPackage.enterpriseMonthly;
        }

        suggestionPackageLabel.setHtmlLabel(Messages.getLabel("buy_credit_suggestion_answer", suggestionPackage.getShortLabel()));
        iabakoPackage.setEnumValue(suggestionPackage);
    }

    private void calculateTotal() {
        int months = ((IabakoPackage) iabakoPackage.getEnumValue()).getTimeInMonths();
        int howMany = howManyField.getIntValue() != null ? howManyField.getIntValue() : 0;

        int totalMonths = months * howMany;

        double contribution = ((IabakoPackage) iabakoPackage.getEnumValue()).getAmount() * howMany;
        for (User user : userListOfMonths.keySet()) {
            SelectList list = userListOfMonths.get(user);
            contribution += 9 * Integer.parseInt(list.getValue() + "");
        }

        if (totalMonths > 0) {
            principalUserDatesPanel.setVisible(true);
            Date expirationDate = getPrincipalExpirationDate();
            Date today = GenericTools.getDateWithoutTime();
            Date startDate;
            Date endDate;
            if (expirationDate.compareTo(today) >= 0) {
                startDate = new GenericTools().addDaysToDate(getPrincipalExpirationDate(), 1);
            } else {
                startDate = today;
            }
            endDate = new GenericTools().addMonthsToDate((Date) startDate.clone(), totalMonths);
            endDate = new GenericTools().addDaysToDate(endDate, -1);

            enterpriseCreditEnds = endDate;
            principalUserDates.setHtmlLabel(Messages.getLabel("buy_credit_dates_title", GenericTools.formatDate(startDate), GenericTools.formatDate(endDate)));

        } else {
            enterpriseCreditEnds = getPrincipalExpirationDate();
            principalUserDatesPanel.setVisible(false);
        }

        if (contribution > 0) {
            totalPanel.setVisible(true);
            confirmationButton.setVisible(true);
            total.setHtmlLabel(Messages.getLabel("buy_credit_total", GenericTools.formatAmount(contribution + "")));
        } else {
            totalPanel.setVisible(false);
            confirmationButton.setVisible(false);
        }
    }

    private void setConfirmationButtonHandler() {

        confirmationButton.getButton().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {

                IabakoPackage selectedIabakoPackage = howManyField.getIntValue() != null && howManyField.getIntValue() > 0 ? (IabakoPackage) iabakoPackage.getEnumValue() : null;

                int customFieldProLimit = IabakoPackage.proMonthly.getCustomFieldLimit();
                int ignoredCustomField = selectedIabakoPackage != null ? enterpriseCustomFields.size() - selectedIabakoPackage.getCustomFieldLimit() : 0;

                if (ignoredCustomField > 0) {

                    List<String> ignoredCustomFields = new ArrayList<String>();
                    for (int i = ignoredCustomField; i > 0; i--) {
                        ignoredCustomFields.add(enterpriseCustomFields.get(enterpriseCustomFields.size() - i).getTitle());
                    }

                    String suggestedPackage = enterpriseCustomFields.size() > customFieldProLimit ? IabakoPackage.enterpriseMonthly.getShortLabel() : IabakoPackage.proMonthly.getShortLabel();

                    String message = Messages.getLabel("buy_credit_lower_than_suggested_message", selectedIabakoPackage.getShortLabel());
                    message += Messages.getLabel("buy_credit_lower_than_suggested_custom_fields", GenericTools.listAsString(ignoredCustomFields) + "");
                    message += Messages.getLabel("buy_credit_lower_than_suggested_reconsider", suggestedPackage);

                    IabakoDialogBox changePackageDialogBox =
                            new IabakoDialogBox("buy_credit_lower_than_suggested_title",
                                    message, "snifficon", true,
                                    "buy_credit_lower_than_suggested_ok", false) {
                                @Override
                                protected void customConfirmButtonHandler() {
                                    goToPayPalForm(false);
                                }
                            };
                    changePackageDialogBox.setAvoidClosePopupCall(true);
                    NavigationController.getInstance().show(changePackageDialogBox);
                    return;
                }

                goToPayPalForm(false);
            }
        });
    }

    private void goToPayPalForm(boolean isUpgrade) {
        UserPayPalToken payPalToken = new UserPayPalToken();
        payPalToken.setUpgradeCredit(isUpgrade);
        payPalToken.setIabakoPackage((IabakoPackage) iabakoPackage.getEnumValue());
        payPalToken.setPayer(AuthenticateUserProvider.getUser());
        payPalToken.setPrincipalUser(principalUser);
        payPalToken.setQuantity(isUpgrade ? 1 : howManyField.getIntValue() != null ? howManyField.getIntValue() : 0);

        //We must pass users in a List because this oneToMany relation is GwtTransient
        List<UserPayPalTokenUser> userTokens = new ArrayList<UserPayPalTokenUser>();
        for (User user : userListOfMonths.keySet()) {
            int howMany = Integer.parseInt(userListOfMonths.get(user).getValue() + "");
            if (howMany > 0) {
                UserPayPalTokenUser userToken = new UserPayPalTokenUser();
                userToken.setUser(user);
                userToken.setQuantity(howMany);
                userTokens.add(userToken);
            }
        }

        userServiceAsync.savePayPalToken(payPalToken, userTokens, new IabakoAsyncCallback<String>() {

            public void onSuccess(String payPalTransactionId) {

                principalPanel.setVisible(false);
                usersPanel.setVisible(false);
                buyCreditPanel.setVisible(false);
                confirmationButton.setVisible(false);
                payPalForm.initialize(payPalTransactionId);
                payPalPanel.setVisible(true);

                super.onSuccess(payPalTransactionId);
            }
        });
    }

    @Override
    protected List<IField> getMandatoryFields() {
        return null;
    }

    @Override
    protected AbstractEntity getEntity() {
        return null;
    }

    @Override
    public boolean validate() {
        return false;
    }
}
